package org.mtmi.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;

import net.n3.nanoxml.IXMLElement;
import net.n3.nanoxml.IXMLParser;
import net.n3.nanoxml.IXMLReader;
import net.n3.nanoxml.StdXMLReader;
import net.n3.nanoxml.XMLException;
import net.n3.nanoxml.XMLParserFactory;

import org.eclipse.swt.widgets.Shell;
import org.mtmi.ui.M;
import org.mtmi.ui.controls.Control;
import org.mtmi.ui.scenes.Scene;
import org.mtmi.ui.scenes.SceneManager;

/**
 * The role of this class is to read a Scene from a file.
 */
public class SceneReader 
{
	private HashMap<Integer, Control> id_to_control;
	
	public SceneReader() 
	{
	}
	
	/** @return the control from it's id, can be used to restore links between controls */ 
	public Control getControl( int id ) 
	{
		if( id_to_control==null ) {
			throw new Error("no control map at this time"); //$NON-NLS-1$
		}
		return id_to_control.get( id );
	}

	public void loadScenes( Shell shell ) 
	{
		String sdir = SceneManager.getDefault().getSceneDirectory();
		
		try {
			if( sdir==null ) {
				throw new ReaderError("No scened directory define: cannot load scenes");
			}

			File fdir = new File( sdir );
			if( !fdir.exists() || !fdir.isDirectory() ) {
				throw new ReaderError("Scene directory does not exists or is not a directory: '"+sdir+"'");
			}

			String files[] = fdir.list( new FilenameFilter(){
				@Override
				public boolean accept( File dir, String name ) {
					return name.endsWith( M.EXT_SCENE );
				}
			});

			System.out.println(">> SCENES FILES: ");
			for( String filename : files ) {
				String fullname = sdir+File.separator+filename;
				System.out.println(" loading scene: "+fullname );
				
				try {
					Scene scene = load( shell, fullname );
					scene.setFileName( filename ); //now scene can also be saved ... later
					SceneManager.getDefault().addScene( scene );
					//scene size must be fixed otherwise getMiniature will failed (0x0)
					scene.setSize( shell.getSize() );
				} catch( ReaderError x ) {
					x.printStackTrace();
				}
			}
		}
		catch( ReaderError x ) {
			x.printStackTrace();
		}
	}
	
	/**
	 * Process load of the scene
	 */
	private Scene load( Shell shell, String fullname ) throws ReaderError
	{
		Scene scene = new Scene( shell );
		id_to_control = new HashMap<Integer, Control>();
		
		//as fileReader() require an URI: filename must not start with 'c:'..=> unknown protocol
		fullname = "/"+fullname; //$NON-NLS-1$
		FileInputStream fis = null;
		try {
			
			XMLReader xr = new XMLReader();
			
			IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
			fis = new FileInputStream( fullname );
			IXMLReader reader = new StdXMLReader( fis );

			parser.setReader(reader);
			IXMLElement e_scene = (IXMLElement) parser.parse();
			
			if( !SceneWriter.E_SCENE.equals( e_scene.getName() ) ) {
				throw new ReaderError("File is not a scene."); //$NON-NLS-1$
			}
			
			//scene background image:
			IXMLElement e_bg = xr.getElement( e_scene, SceneWriter.E_BACKGROUND, false );
			if( e_bg!=null ) {
				String key = xr.getAttrString( e_bg, SceneWriter.A_KEY );
				scene.setBackgroundKey( key );
			}
			
			//create controls, keep id's in map
			IXMLElement e_controls = xr.getElement( e_scene, SceneWriter.E_CONTROLS, true );
			for( Object n : e_controls.getChildren() ) {
				if( n instanceof IXMLElement ) {
					IXMLElement e = (IXMLElement)n;
					if( SceneWriter.E_CONTROL.equals( e.getName() ) ) {
						int id = xr.getAttrInt( e, SceneWriter.A_ID );
						String cls = xr.getAttrString( e, SceneWriter.A_CLASS );
						int flags = xr.getAttrInt( e, SceneWriter.A_FLAGS );
						Control c = ControlFactory.create( scene, flags, cls );
						if( c==null ) {
							throw new ReaderError( "No factory known to create control: "+cls );
						}
						id_to_control.put( id, c );
					}
				}
			}
			
			//load controls data:
			IXMLElement e_contents = xr.getElement( e_scene, SceneWriter.E_CONTENTS, true );
			for( Object n : e_contents.getChildren() ) {
				if( n instanceof IXMLElement ) {
					IXMLElement e = (IXMLElement)n;
					if( SceneWriter.E_DATA.equals( e.getName() ) ) {
						int id = xr.getAttrInt( e, SceneWriter.A_CONTROL );
						Control c = id_to_control.get( id );
						if( c==null ) {
							throw new ReaderError("control data id:"+id+" is not a control");
						}
						
						IControlIO creader = (IControlIO)c.getAdapter( IControlIO.class, null );
						if( creader!=null ) {
							creader.readControl( c, e, xr, this );
						}
					}
				}
			}
			
			//reset id map:
			id_to_control = null;
			
			return scene;
		} catch( IOException x ) {
			throw new ReaderError( x.getMessage() );
		} catch( IllegalAccessException x ) {
			throw new ReaderError( x.getMessage() );
		} catch( InstantiationException x ) {
			throw new ReaderError( x.getMessage() );
		} catch( ClassNotFoundException x ) {
			throw new ReaderError( x.getMessage() );
		} catch( XMLException x ) {
			throw new ReaderError( x.getMessage() );
		}
		finally {
			if( fis!=null ) {
				try {
					fis.close();
				} catch( IOException x ) {
					x.printStackTrace();
				}
			}
		}
	}
}
